﻿#ifndef _CCROLE_INCLUDE
#define _CCROLE_INCLUDE


#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

#include "CCRoleOutlineUtils.hlsl"

struct Attributes{
    float3 positionOS        : POSITION;
    half3  normalOS          : NORMAL;
    half4 tagentOS           : TANGENT;
    float2 uv                : TEXCOORD0;
};


struct Varyings{
    float2 uv                      : TEXCOORD0;
    float4 positionWSAndFogFactor  : TEXCOORD1;
    half3 normalWS                 : TEXCOORD2;
#ifdef _MAIN_LIGHT_SHADOWS
    float4 shadowCoord             : TEXCOORD3;
#endif
    float4 positionCS              : SV_POSITION;
};


///////////////////////////////////////////////////////////////////////////////////////
// CBUFFER and Uniforms 
// (you should put all uniforms of all passes inside this single UnityPerMaterial CBUFFER! else SRP batching is not possible!)
///////////////////////////////////////////////////////////////////////////////////////

sampler2D _BaseMap;
sampler2D _EmissionMap;
#ifdef ToonShaderIsOutline
    sampler2D _OutlineZOffsetMaskTex;
#endif

// put all your uniforms(usually things inside .shader file's properties{}) inside this CBUFFER, in order to make SRP batcher compatible
// see -> https://blogs.unity3d.com/2019/02/28/srp-batcher-speed-up-your-rendering/
CBUFFER_START(UnityPerMaterial)
    // base color
    float4 _BaseMap_ST;
    half4 _BaseColor;
    
    // emission
    float _UseEmission;
    half _EmissionMulByBaseColor;
    half4 _EmissionColor;
    half3 _EmissionMapChannelMask;
    
    
    // lighting
    half3 _IndirectLightConstColor;
    half _IndirectLightMultiplier;
    
    half _DirectLightMultiplier;
    half _CelShadeMidPoint;
    half _CelShadeSoftness;
    half _MainLightIgnoreCelShade;
    half _AdditionalLightIgnoreCelShade;
    
    
    //shadowmap
    half _ReceiveShadowMappingAmount;
    
    // outline 
    float _OutlineWidth;
    half3 _OutlineColor;
    float _OutlineZOffst;
    float _OutlineZOffsetMaskRemapStart;
    float _OutlineZOffsetMaskRemapEnd;
    
    
    

CBUFFER_END


struct SurfaceData{
    half3 albedo;
    half alpha;
    half3 emission;
  
};


struct LightingData{
    half3 normalWS;
    float3 positionWS;
    half3 viewDirectionWS;
#ifdef _MAIN_LIGHT_SHADOWS
    float4 shadowCoord;
#endif
};

half invLerpClamp(half from, half to, half value)
{
    half invLerp = (value - from) / (to - from);
    return saturate(invLerp);
}

SurfaceData InitSurfaceData(Varyings input){
    SurfaceData output;
    
    float4 baseColor = tex2D(_BaseMap, input.uv) * _BaseColor;
    // 主要贴图颜色
    output.albedo = baseColor.rgb;
    output.alpha = baseColor.a;
    
    // 边缘光
    if(_UseEmission){
        output.emission = tex2D(_EmissionMap, input.uv).rgb * _EmissionColor.rgb * _EmissionMapChannelMask;
    }
    else{
        output.emission = 0;
    }
    // 
    
    return output;
}

half3 ShadeEmission(SurfaceData surfaceData, LightingData lightingData){
    half3 emissionResult = lerp(surfaceData.emission, surfaceData.emission * surfaceData.albedo,_EmissionMulByBaseColor);
    return emissionResult;
}

half3 ShadeMainLight(SurfaceData surfaceData, LightingData lightingData, Light light,bool isAdditionalLight){
    half3 N = lightingData.normalWS;
    half3 L = light.direction;
    half3 V = lightingData.viewDirectionWS;
    half3 H = normalize(L + V);
    half NDotL = dot(N, L);
    half lightAttenuation = 1;
    lightAttenuation *= lerp(1, light.shadowAttenuation, _ReceiveShadowMappingAmount);
    
    // N dot L
    // simplest 1 line cel shade, you can always replace this line by your own better method !
    half celShadeResult = smoothstep(_CelShadeMidPoint-_CelShadeSoftness,_CelShadeMidPoint+_CelShadeSoftness, NDotL);
    
    lightAttenuation *= lerp(celShadeResult, 1, isAdditionalLight?_AdditionalLightIgnoreCelShade:_MainLightIgnoreCelShade);
    
    lightAttenuation *= _DirectLightMultiplier;
    
    return light.color * lightAttenuation;
}

half3 CompositeAllLightResults(half3 indirectResult, half3 mainLightResult, half3 additionalLightSumResult, half3  emissionResult, SurfaceData surfaceData, LightingData lightingData){
    half3 rawLightSum = max(indirectResult, mainLightResult + additionalLightSumResult);
    half lightLuminance = Luminance(rawLightSum);
    half3 finalLightMulResult = rawLightSum / max(1, lightLuminance / max(1, log(lightLuminance)));
    return surfaceData.albedo * finalLightMulResult + emissionResult;
}

half3 ShadeAllLights(SurfaceData surfaceData, LightingData lightingData){
    // Indirect lighting
    
    // hide 3D feeling by ignore all detail SH
    // SH 1 (only use this)
    // SH 234 (ignored)
    // SH 56789 (ignored)
    // we just want to tint some average envi color only
    half3 averageSH = SampleSH(0);
    
    half3 indirectResult = surfaceData.albedo * (_IndirectLightConstColor + averageSH * _IndirectLightMultiplier);
        
    //  light
    
    Light mainLight;
#ifdef _MAIN_LIGHT_SHADOWS
    mainLight = GetMainLight(lightingData.shadowCoord);
#else
    mainLight = GetMainLight();
#endif

    half3 mainLightResult = ShadeMainLight(surfaceData, lightingData, mainLight,false) ;
    
    // All Additional ShadeAllLights
    half3 additionalLightSumResult = 0;
    
#ifdef _ADDITIONAL_LIGHTS
    int additionalLightCount = GetAdditionalLightsCount();
    // TODO 后续多光源的时候在做
#endif    
    half3 emissionResult = ShadeEmission(surfaceData, lightingData);
    
    return CompositeAllLightResults(indirectResult, mainLightResult, additionalLightSumResult, emissionResult, surfaceData, lightingData);
    
    
}

float3 TransformPositionWSToOutlinePositionWS(float3 positionWS, float positionVS_Z, float3 normalWS){
    float outlineExpandAmount = _OutlineWidth * GetOutlineCameraFovAndDistanceFixMultiplier(positionVS_Z);
    return positionWS + normalWS * outlineExpandAmount;
}

// Useful for:
// -Hide ugly outline on face/eye
// -Make eyebrow render on top of hair
// -Solve ZFighting issue without moving geometry
float4 NiloGetNewClipPosWithZOffset(float4 originalPositionCS, float viewSpaceZOffsetAmount)
{
    if(unity_OrthoParams.w == 0)
    {
        ////////////////////////////////
        //Perspective camera case
        ////////////////////////////////
        float2 ProjM_ZRow_ZW = UNITY_MATRIX_P[2].zw;
        float modifiedPositionVS_Z = -originalPositionCS.w + -viewSpaceZOffsetAmount; // push imaginary vertex
        float modifiedPositionCS_Z = modifiedPositionVS_Z * ProjM_ZRow_ZW[0] + ProjM_ZRow_ZW[1];
        originalPositionCS.z = modifiedPositionCS_Z * originalPositionCS.w / (-modifiedPositionVS_Z); // overwrite positionCS.z
        return originalPositionCS;    
    }
    else
    {
        ////////////////////////////////
        //Orthographic camera case
        ////////////////////////////////
        originalPositionCS.z += -viewSpaceZOffsetAmount / _ProjectionParams.z; // push imaginary vertex and overwrite positionCS.z
        return originalPositionCS;
    }
}

half3 ConvertSurfaceColorToOutlineColor(half3 originalSurfaceColor)
{
    return originalSurfaceColor * _OutlineColor;
}


Varyings VertexRole(Attributes input){
    Varyings output;
    
    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS);
   
    VertexNormalInputs vertexNormalInput = GetVertexNormalInputs(input.normalOS,input.tagentOS);
    
    
    float3 positionWS = vertexInput.positionWS;
    
#ifdef ToonShaderIsOutline
    positionWS = TransformPositionWSToOutlinePositionWS(vertexInput.positionWS, vertexInput.positionVS.z, vertexNormalInput.normalWS);
#endif
    
    float fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    
    //uv
    output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
    
    output.positionWSAndFogFactor = float4(positionWS, fogFactor);
    output.normalWS = vertexNormalInput.normalWS;
    
    output.positionCS = TransformWorldToHClip(positionWS);

#ifdef ToonShaderIsOutline
    float outlineZOffsetMasTexExplicMipLevel = 0;
    float outlineZOffsetMask = tex2Dlod(_OutlineZOffsetMaskTex, float4(input.uv, 0, outlineZOffsetMasTexExplicMipLevel)).r;
    
    outlineZOffsetMask = 1 - outlineZOffsetMask;
    outlineZOffsetMask = invLerpClamp(_OutlineZOffsetMaskRemapStart, _OutlineZOffsetMaskRemapEnd, outlineZOffsetMask);
    
    output.positionCS = NiloGetNewClipPosWithZOffset(output.positionCS, _OutlineZOffst * outlineZOffsetMask);
    
#endif
#ifdef _MAIN_LIGHT_SHADOWS
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif

    
    return output;

}

half4 FragRole(Varyings input) : SV_TARGET{
    
    SurfaceData surfaceData = InitSurfaceData(input);
    
    LightingData lightingData;
    lightingData.positionWS = input.positionWSAndFogFactor.xyz;
    lightingData.viewDirectionWS = SafeNormalize(GetCameraPositionWS() - lightingData.positionWS);
    lightingData.normalWS = normalize(input.normalWS);
    
#ifdef _MAIN_LIGHT_SHADOWS
    lightingData.shadowCoord = input.shadowCoord;
#endif
  
    half3 color = ShadeAllLights(surfaceData, lightingData);

#ifdef ToonShaderIsOutline
    color = ConvertSurfaceColorToOutlineColor(color);
#endif
       
    //color = ApplyFog(color, input);
    
    
    return half4(color,surfaceData.alpha);
}





#endif